/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 combat_footprint.cpp

	$Header: /heroes4/combat_footprint.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "combat_footprint.h"

#include <assert.h>
#include <map>
#include "map_point.h"
#include "map_rect.h"

// -------------------------------------------------------------------------
// footprint of combat object, marked by cell as to whether a cell is used.
// -------------------------------------------------------------------------
t_combat_footprint::t_combat_footprint()
{
}

t_combat_footprint::t_combat_footprint( int size )
{
	int		row;
	int		column;
	int		delta_row;
	int		delta_column;
	int		delta_row_squared;
	int		radius_squared;
	int		distance;
	int*	ptr;

	m_size = size;
	m_marked.resize( size * size );  
  radius_squared = size * size + 2;
  ptr = ((m_marked.size() > 0) ? &(*m_marked.begin()) : NULL);
  delta_row = 1 - size;
  for (row = 0; row < size; row++)
  {
    delta_row_squared = delta_row * delta_row;
    delta_column = 1 - size;
    for (column = 0; column < size; column++)
    {
      distance = delta_column * delta_column + delta_row_squared;
      *(ptr++) = (distance <= radius_squared ? 1 : 0);
      delta_column += 2;
    }
    delta_row += 2;
  }
}

// -------------------------------------------------------------------------
// footprint defining points of intersection of two combat objects
// -------------------------------------------------------------------------
t_combat_footprint::t_combat_footprint( t_combat_footprint const& fixed_footprint, 
										t_combat_footprint const& moving_footprint )
{
	int				fixed_size = fixed_footprint.get_size();
	int				moving_size = moving_footprint.get_size();
	t_map_point_2d	offset;
	int*			ptr;

	m_size = fixed_size + moving_size - 1;
	m_marked.resize( m_size * m_size );
  ptr = ((m_marked.size() > 0) ? &(*m_marked.begin()) : NULL);
	for (offset.row = -moving_size + 1; offset.row < fixed_size; ++offset.row)
	{
		for (offset.column = -moving_size + 1; offset.column < fixed_size; ++offset.column)
		{
      *(ptr++) = (moving_footprint.overlaps( offset, fixed_footprint ) ? 1 : 0);
		}
	}
}

// maintain an initialized array of footprints
t_combat_footprint const& get_combat_footprint( int size )
{
	static std::map< int, t_combat_footprint >		footprints;
	std::map< int, t_combat_footprint >::iterator	result;

	result = footprints.find( size );
	if (result == footprints.end())
	{
		t_combat_footprint footprint( size );

		result = footprints.insert( std::make_pair( size, footprint ) ).first;
	}
	return result->second;
}

// ------------------------------------------------------------------------
// check if two footprints overlap if first footprint is at (offset) from second
// ------------------------------------------------------------------------
bool t_combat_footprint::overlaps( t_map_point_2d const& point,
								   t_combat_footprint const& right ) const
{
	t_map_point_2d offset;
	t_map_point_2d new_point;
	int            footprint_size = get_size();
	int            right_size = right.get_size();
	int const*    ptr;
	int const*    right_ptr;
	t_map_rect_2d  rect;

	// determine area of overlap
	rect.top_left.column = -point.column;
	if (rect.top_left.column < 0)
		rect.top_left.column = 0;
	rect.bottom_right.column = right_size - point.column;
	if (rect.bottom_right.column > footprint_size)
		rect.bottom_right.column = footprint_size;
	if (rect.bottom_right.column <= rect.top_left.column)
		return false;

	rect.top_left.row = -point.row;
	if (rect.top_left.row < 0)
		rect.top_left.row = 0;
	rect.bottom_right.row = right_size - point.row;
	if (rect.bottom_right.row > footprint_size)
		rect.bottom_right.row = footprint_size;

	for (offset.row = rect.top(); offset.row < rect.bottom(); offset.row++)
	{
		ptr = &(*(m_marked.begin() + offset.row * m_size));
		assert( point.row + offset.row >= 0 );
		assert( point.row + offset.row < right_size );
		right_ptr = right[point.row + offset.row];
		for (offset.column = rect.left(); offset.column < rect.right(); offset.column++)
		{
			assert( point.column + offset.column >= 0 );
			assert( point.column + offset.column < right_size );
			if (!ptr[offset.column])
				continue;
			if (!right_ptr[point.column + offset.column])
				continue;
			return true;
		}
	}
	return false;
}

// ------------------------------------------------------------------------
// get area which reflects overlaps of two combat footprints
// ------------------------------------------------------------------------
namespace
{
	struct t_footprint_key
	{
		t_footprint_key( int fixed_arg, int moving_arg );

		int fixed_size;
		int moving_size;
	};

	inline t_footprint_key::t_footprint_key( int fixed_arg, int moving_arg )
	{
		fixed_size = fixed_arg;
		moving_size = moving_arg;
	}

	inline bool operator < ( t_footprint_key const& left, t_footprint_key const& right )
	{
		if (left.fixed_size != right.fixed_size)
			return left.fixed_size < right.fixed_size;
		return left.moving_size < right.moving_size;
	}

	typedef std::map< t_footprint_key, t_combat_footprint > t_overlap_map;
};

// ------------------------------------------------------------------------
// get area which reflects overlaps of two combat footprints from a cache of 
// such information
// ------------------------------------------------------------------------
t_combat_footprint const& get_footprint_overlap( int fixed_size, int moving_size )
{
	static t_overlap_map	overlap_footprints;
	t_footprint_key			key( fixed_size, moving_size );
	t_overlap_map::iterator result = overlap_footprints.find( key );

	if (result == overlap_footprints.end())
	{
		t_combat_footprint footprint( get_combat_footprint( fixed_size ),
									  get_combat_footprint( moving_size ));
		result = overlap_footprints.insert( std::make_pair( key, footprint ) ).first;
	}
	return result->second;
}
